<?php
/* 
This is library file to payment modules. Cant be reused to 
communicate with banks.

Copyright (c) 2010 Ingvar Harjaks <ingvarharjaks@gmail.com>
Copyright (c) 2011 Leone Viru <leone.viru@gmail.com>
All costs of this development were covered by open source fiendy
company BrightSide OÜ <www.brightside.ee>

Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Pizza. If not, see <http://www.gnu.org/licenses/>.
*/

class Pizza extends Controller {
  public $bank;
  public $s_id; /* Unique service identifier */
  public $vk_version;  /* I-pizza framework ver */
  public $form_data = array(); // internal to hold additional form data sent to banks

  /* Enables to add additional post form data */
  public function addFormData($parameter,$value) {
    $this->form_data[$parameter] = $value;
  }
  
  /* Parses additional post data to html */
  public function getFormDataHTML() {
    /*
      render sample:
      <input type="hidden" name="VK_RETURN" value="<?php echo $VK_RETURN; ?>" />
    */
    $buf = '';
    foreach ($this->form_data as $parameter=>$value) {
      $buf .= "<input type=\"hidden\" name=\"$parameter\" value=\"$value\" />\n";
    }
    return $buf;
  }
  
  /* Service fileds in right order for service you need */
  private function get_service_fieds() {
    $services = array();
    $services["1001"] = array("VK_SERVICE", "VK_VERSION", "VK_SND_ID", 
			     "VK_STAMP", "VK_AMOUNT", "VK_CURR", "VK_ACC", 
			     "VK_NAME", "VK_REF", "VK_MSG");
    $services["1002"] = array("VK_SERVICE", "VK_VERSION", "VK_SND_ID", 
			     "VK_STAMP", "VK_AMOUNT", "VK_CURR", "VK_REF", 
			      "VK_MSG");
    $services["1101"] = array("VK_SERVICE", "VK_VERSION", "VK_SND_ID", 
			     "VK_REC_ID", "VK_STAMP", "VK_T_NO",
			     "VK_AMOUNT", "VK_CURR", "VK_REC_ACC", 
			     "VK_REC_NAME", "VK_SND_ACC", "VK_SND_NAME", 
			     "VK_REF", "VK_MSG", "VK_T_DATE");
    $services["1901"] = array("VK_SERVICE", "VK_VERSION", "VK_SND_ID", 
			     "VK_REC_ID", "VK_STAMP", "VK_REF", "VK_MSG");
    $services["1902"] = array("VK_SERVICE", "VK_VERSION", "VK_SND_ID", 
			     "VK_REC_ID", "VK_STAMP", "VK_REF", "VK_MSG", 
			     "VK_ERROR_CODE");
    
    if (array_key_exists($this->s_id, $services)) {
      return $services[$this->s_id];
    } else {
      $this->_error(sprintf("No such service: %s!", $this->s_id));
      return false;
    }
  }

  /* Returns all data of currently processed order */
  private function get_order_info($order_id=false) {
    if ($order_id == false) {
      $order_id = $this->session->data['order_id'];
    }
    $this->load->model('checkout/order');
    $order_info = $this->model_checkout_order->getOrder($order_id);
    return $order_info;
  }

  /* Collect all required data to be forwarded to bank and put it
     to template. */
  protected function prepare_data() {
    $this->language->load('common/pizza');
    $this->language->load(sprintf('payment/%s', $this->bank));
    $order_info = $this->get_order_info();
    $this->id = 'payment';

    $this->data["s_id"] = $this->s_id;

    /* Calculate currency to EUR not mather what it is */
    $amount = $this->currency->format($order_info['total'], "EUR", '', false);

    $this->data['button_confirm'] = $this->language->get('button_confirm');
    $this->data['button_back'] = $this->language->get('button_back');

    $this->data["VK_SERVICE"] = $this->s_id;
    $this->data["VK_VERSION"] = $this->vk_version;
    $this->data["VK_SND_ID"] = $this->config->
      get(sprintf("%s_username", $this->bank)); 
    $this->data["VK_STAMP"] = $order_info['order_id'];
    $this->data["VK_AMOUNT"] = $amount;
    $this->data["VK_CURR"] = "EUR";
    $this->data["VK_ACC"] = $this->config->
      get(sprintf("%s_vk_acc", $this->bank));       
    $this->data["VK_NAME"] = $this->config->
      get(sprintf("%s_vk_name", $this->bank));       
    $this->data["VK_REF"] = "";
    $this->data["VK_MSG"] = $this->language->get('text_msg').' '.$order_info['order_id'];
    
      if($this->bank == 'paysampo') {
	foreach($this->data as $f=>$v) {
		$this->data[$f] = utf8_decode(mb_convert_encoding($v,'iso-8859-1','utf-8'));
	}

    }  
    
    
    $mac = $this->make_mac($this->data);
    


    /* swedbank requires VK_ENCODING  as post parameter for encoding data */
    if($this->bank == 'payswed') {
      $this->addFormData('VK_ENCODING','utf-8');
    }
    /* seb requires VK_CHARSET as post parameter */
    if($this->bank == 'payseb') {
      $this->addFormData('VK_CHARSET','utf-8');
    }
    /* krediidipank requires VK_CHARSET as post parameter for encoding data */
    if($this->bank == 'paykred') {
      $this->addFormData('VK_CHARSET','utf-8');
    }

    
    
    $this->data["VK_MAC"] = $this->sign_mac($mac);
    $this->data['VK_RETURN'] = HTTPS_SERVER.
      sprintf("index.php?route=payment/%s/callback", $this->bank);    
    $this->data["VK_LANG"] = $this->get_language();
    $this->data['action'] = $this->config->
      get(sprintf("%s_bank_url", $this->bank));       
    $this->data['back'] = HTTPS_SERVER.
      sprintf("index.php?route=checkout/%s", $this->bank);    

    /* add additonal form data */
    $this->data['form_data'] = $this->getFormDataHTML();
    /* Loads tpl file and puts all data gathered before into it. */
    $this->template = $this->config->get('config_template').
      '/template/common/pizza.tpl';
    $this->render();
    return;
  }

  /* Mac - just bunch of data. */
  private function make_mac($data_arr) {
    $mac = '';
    $service_fields = $this->get_service_fieds($this->s_id);
    foreach ($service_fields as $field) {
      if (!array_key_exists($field, $data_arr)) {
	$this->_error("No data to gen mac"); return false;
      } else {
      
        /* swedbanks need extra handling with mb_strlen */
        if($this->bank == 'payswed') {
          $mac .= str_pad(mb_strlen(mb_convert_encoding($data_arr[$field],'utf-8','utf-8'),'utf-8'), 3, '0', 
          STR_PAD_LEFT).$data_arr[$field];
        }
        elseif($this->bank == 'payseb') {
          $mac .= str_pad(strlen(mb_convert_encoding($data_arr[$field],'utf-8','utf-8')), 3, '0', 
          STR_PAD_LEFT).$data_arr[$field];
        }
        elseif($this->bank == 'paysampo'){
          /* this bank expects fields as iso-8859-1 */
          //$mac .= str_pad(strlen(mb_convert_encoding($data_arr[$field],'iso-8859-1','utf-8')), 3, '0', 
          //STR_PAD_LEFT).$data_arr[$field];
					$mac .= str_pad(strlen($data_arr[$field]), 3, '0', 
					STR_PAD_LEFT).$data_arr[$field];
					
        }
        elseif($this->bank == 'paykred') {
          $mac .= str_pad(strlen(mb_convert_encoding($data_arr[$field],'utf-8','utf-8')), 3, '0', 
          STR_PAD_LEFT).$data_arr[$field];
        }    
        /* otherwise use this for mack making THIS WILL BECOME IMPOSSIBLE SITUATION NOW */
        else {
					$mac .= str_pad(strlen($data_arr[$field]), 3, '0', 
					STR_PAD_LEFT).$data_arr[$field];
        }
      }
    }
    return $mac;
  }

  /* Verify mac by bank's public key */
  private function verify_mac($mac_gend, $mac_sent) {
    $key_file = file_get_contents($this->config->
				  get(sprintf('%s_key_pub', $this->bank)));
    $key = openssl_pkey_get_public($key_file);
    if (!openssl_verify($mac_gend, base64_decode($mac_sent), $key)) {
      return false;
    }
    return true;
  }

  /* Sign mac by private key */
  private function sign_mac($mac) {
    $key_file = file_get_contents($this->config->
				  get(sprintf('%s_key_sec', $this->bank)));
    if (strlen($this->config->get(sprintf('%s_key_sec_pw', 
					  $this->bank))) != 0) {
      $key_file_pw = $this->config->get(sprintf('%s_key_sec_pw', $this->bank));
      $key = openssl_pkey_get_private($key_file, $key_file_pw);
    } else {
      $key = openssl_pkey_get_private($key_file);
    }
    if (!openssl_sign($mac, $sig, $key)) {
      $this->_error("OpenSSL refuses to sign mac"); return false;
    }
    return base64_encode($sig);
}

  /* Put errors to log and show nice maintance text to end-user */
  private function _error($msg, $cancelled=false) {
    $this->language->load('common/pizza');
    if ($cancelled == false) {
      error_log($msg);
      $this->data["error_title"] = $this->language->get('error_title');
      $this->data["error_desc"] = $this->language->get('error_desc');
    } else {
      $this->data["error_title"] = $this->language->get('cancel_title');
      $this->data["error_desc"] = $this->language->get('cancel_desc');
    }
      $this->data["error_wait"] = $this->language->get('error_wait');
      $this->data['continue'] = HTTPS_SERVER.sprintf("index.php?route=checkout/cart");
      $this->template = $this->config->get('config_template').
	'/template/common/pizza_failture.tpl';
    
      $this->response->setOutput($this->render(TRUE), 
				 $this->config->get('config_compression'));
  }

  /* Triest to convert language from session to iso format language code
     used in bank-s interface. */
  private function get_language() {
    switch ((string)$_SESSION["language"]) {
    case "et":
      $lng = "EST";
      break;
    case "ru":
      $lng = "RUS";
      break;
    default:
      return "ENG";
      break;
  }
    return $lng;
  }

  /* Response from bank lands here */
  protected function pizza_callback() {
    $this->language->load(sprintf('payment/%s', $this->bank));
    $fields = array();
      /* Get needed returned data (VK_) to variable */
    foreach ((array)$_REQUEST as $f => $v) {
    
         // if($this->bank == 'paysampo') {

		//$this->data[$f] = mb_convert_encoding($v,'iso-8859-1','utf-8');


   // } 
    
    
    
    
      if (substr ($f, 0, 3) == 'VK_') {
	$fields[$f] = $v;
      }
    } 
      /* I need at least service id to continue */
    if (!isset($fields["VK_SERVICE"])) {
      $this->_error("Not enough data recieved");
      return false;
    }
    $this->s_id = (string)$fields["VK_SERVICE"];
      /* Check if signature is correct */
    $mac_gend = $this->make_mac($fields);
    if ($this->verify_mac($mac_gend, (string)$fields["VK_MAC"]) == false) {
      $this->_error("MAC validation failed");
      return false;
    }
      /* If bank error or user cancelled */
    if ($this->s_id != 1101) {
      $this->_error("User/bank cancelled!", true);
      return false;
    }
      /* The following will accept payment */
    $this->load->model('checkout/order');	  
    $order_info = $this->get_order_info($fields["VK_STAMP"]);
    $order_status_id = $this->config->get(sprintf('%s_order_status_id', 
						  $this->bank));
    $this->model_checkout_order->confirm($order_info["order_id"], 
					 $order_status_id);
    $this->redirect(HTTPS_SERVER.
		    sprintf("index.php?route=checkout/success"));
    return;
  }
  
}